/* 目录或者文件操作 */
const fs = require('fs');
const path = require('path');
const {
  bytes2KB
} = require('@lib/utils/tools');
class Fs {
  /**
   * 遍历目录下的全部文件
   * @param {*} dir 指定目录
   * @param {*} fileList 文件列表，每一项包括：文件名fileName、文件路径filePath、扩展名ext、文件大小size(KB)
   */
  static traverseDirectory(dir, fileList = []) {
    // 读取目录中的所有文件和子目录
    const files = fs.readdirSync(dir);
    // 遍历每个文件和子目录
    for (const file of files) {
      const filePath = path.join(dir, file);
      const stat = fs.statSync(filePath);
      // 判断是否为文件
      if (stat.isFile()) {
        fileList.push({
          fileName: file,
          ext: path.extname(file).toLowerCase(),
          size: bytes2KB(stat.size),
          filePath: filePath.replace(/\\/g, '/')
        });
      } else {
        // 是目录，则递归遍历子目录
        Fs.traverseDirectory(filePath, fileList);
      }
    }
  }

  /**
   * 给定目录路径，校验该目录是否存在
   * @param {*} dirPath 目录路径
   * @returns 是否存在该目录-布尔值
   */
  static isDirectoryExists(dirPath) {
    try {
      fs.accessSync(dirPath, fs.constants.F_OK);
      const stats = fs.statSync(dirPath);
      return stats.isDirectory();
    } catch (err) {
      return false;
    }
  }

  /**
   * 在当前项目下创建目录
   * @param {*} dirName 目录名称
   * @param {*} cb 回调函数
   */
  static mkDir(directoryPath, cb = null) {
    fs.mkdir(directoryPath, { recursive: true }, (err) => {
      if (err) {
        throw err;
      }
      cb && cb(directoryPath);
    });
  }

  /**
   * 追加写入内容到文件中
   * @param {*} directoryPath 目录路径
   * @param {*} fileName 文件名
   * @param {*} content 内容(一般是字符串格式)
   * @param {*} cb 回调函数
   */
  static appendData2File(directoryPath, fileName, content, cb = null) {
    // 定义追加写入内容到文件处理函数
    const appendFileHandler = (finalDirPath) => {
      const newFilePath = finalDirPath ? path.join(finalDirPath, fileName) : fileName;
      // 追加写入文件
      fs.appendFile(newFilePath, content, (err) => {
        if (err) {
          throw err;
        }
        console.log(`${newFilePath}文件追加写入完成`);
        cb && cb(newFilePath);
      });
    };
    // 传参指定目录存在，说明需要创建目录并写入该目录下的文件
    if (directoryPath) {
      // 判断目录是否存在，不存在则创建目录再写入内容，存在则直接写入
      if (!Fs.isDirectoryExists(directoryPath)) {
        Fs.mkDir(directoryPath, (finalDirPath) => {
          appendFileHandler(finalDirPath);
        });
        return;
      }
      appendFileHandler(directoryPath);
      return;
    }
    // 传参指定目录不存在，说明直接将文件写在当前目录下
    appendFileHandler('');
  }

  /**
   * 写入内容到文件中
   * @param {*} fileName 文件名
   * @param {*} content 内容(一般是字符串格式)
   */
  static writeData2FileSync(fileName, content) {
    return new Promise((_resolve, _reject) => {
      fs.writeFile(fileName, content, 'utf8', (err) => {
        if (err) {
          console.error(`写入 ${fileName} 文件失败: ${err}`);
          _resolve(false);
        }
        console.log(`写入 ${fileName} 文件成功`);
        _resolve(true);
      });
    });
  }

  /**
   * 清空某个目录下的全部文件
   * @param {*} directoryPath 目录路径
   */
  static emptyDirectory(directoryPath) {
    if (fs.existsSync(directoryPath)) {
      const files = fs.readdirSync(directoryPath);
      for (const file of files) {
        const filePath = path.join(directoryPath, file);
        if (fs.lstatSync(filePath).isDirectory()) {
          // 递归删除子目录中的内容
          Fs.emptyDirectory(filePath);
          // 删除空子目录
          fs.rmdirSync(filePath);
        } else {
          // 删除文件
          fs.unlinkSync(filePath);
        }
      }
    }
  }

  /**
   * 删除项目下除指定目录和文件外的其他目录和文件
   * @param {*} projectPath 项目路径
   * @param {*} exceptDirsFiles 指定目录和文件列表
   */
  static deleteDirsAndFilesNotInList(projectPath, exceptDirsFiles = []) {
    const files = fs.readdirSync(projectPath);
    files.forEach((file) => {
      if (!exceptDirsFiles.includes(file)) {
        const filePath = path.join(projectPath, file);
        const isDirectory = fs.lstatSync(filePath).isDirectory();
        if (isDirectory) {
          // 删除目录
          Fs.emptyDirectory(filePath);
          // 删除空目录
          fs.rmdirSync(filePath);
        } else if (!isDirectory) {
          // 删除文件
          fs.unlinkSync(filePath);
        }
      }
    });
  }

  /**
   * 同步读取文件内容
   * @param {*} filePath 文件路径
   * @param {*} format 格式，默认取值utf8
   * @returns 文件内容
   */
  static readFileSync(filePath, format = 'utf8') {
    const syncData = fs.readFileSync(filePath, format);
    return syncData;
  }

  /**
   * 通过文件的相对路径获取文件名称
   * @param {*} relativeFilePath 文件相对路径
   * @returns 文件名称
   */
  static getFileNameByRelativePath(relativeFilePath) {
    return path.basename(relativeFilePath);
  }

  /**
   * 判断给定文件是否存在当前目录下
   * @param {*} fileName 文件名称
   * @returns 给定文件是否存在当前目录下-布尔值
   */
  static isFileExistsInCurDir(fileName) {
    // 获取当前目录的路径
    const currentDirectory = process.cwd();
    // 拼接文件路径
    const filePath = path.join(currentDirectory, fileName);
    // 检查文件是否存在
    try {
      fs.accessSync(filePath, fs.constants.F_OK);
      return true;
    } catch (err) {
      return false;
    }
  }
}
module.exports = Fs;
